package middleware

import (
	"bytes"
	"encoding/json"

	"io"
	"log"
	"time"

	"github.com/gin-gonic/gin"
)

// bodyLogWriter is a custom response writer that captures the response body
type bodyLogWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

// Write captures the response body while writing it
func (w bodyLogWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

// Logger is a middleware function that logs the request and response details
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Start timer
		start := time.Now()

		// Read the request body
		var requestBody []byte
		if c.Request.Body != nil {
			requestBody, _ = io.ReadAll(c.Request.Body)
			// Restore the request body
			c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
		}

		// Create a buffer to capture the response
		responseBody := &bytes.Buffer{}
		blw := &bodyLogWriter{body: responseBody, ResponseWriter: c.Writer}
		c.Writer = blw

		// Format request body for logging
		var prettyRequest bytes.Buffer
		if len(requestBody) > 0 {
			if err := json.Indent(&prettyRequest, requestBody, "", "  "); err != nil {
				prettyRequest.Write(requestBody) // Use raw body if not JSON
			}
		}

		// Log the request
		log.Printf("[HTTP Request] %s %s\nHeaders: %v\nBody: %s",
			c.Request.Method,
			c.Request.URL.Path,
			c.Request.Header,
			prettyRequest.String())

		// Process request
		c.Next()

		// Calculate duration
		duration := time.Since(start)

		// Format response body for logging
		var prettyResponse bytes.Buffer
		if responseBody.Len() > 0 {
			if err := json.Indent(&prettyResponse, responseBody.Bytes(), "", "  "); err != nil {
				prettyResponse.Write(responseBody.Bytes()) // Use raw body if not JSON
			}
		}

		// Log the response
		log.Printf("[HTTP Response] %s %s\nStatus: %d\nDuration: %v\nHeaders: %v\nBody: %s",
			c.Request.Method,
			c.Request.URL.Path,
			c.Writer.Status(),
			duration,
			c.Writer.Header(),
			prettyResponse.String())
	}
}
